/*
 * @(#)EventThread.java	1.6 05/11/17
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright (c) 1997-2001 by Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 * 
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

package nl.sogyo.objectvisualisatie.trace;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import nl.sogyo.objectvisualisatie.core.DataFacade;

import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.Field;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.Locatable;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventIterator;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.ExceptionEvent;
import com.sun.jdi.event.MethodEntryEvent;
import com.sun.jdi.event.MethodExitEvent;
import com.sun.jdi.event.ModificationWatchpointEvent;
import com.sun.jdi.event.StepEvent;
import com.sun.jdi.event.ThreadDeathEvent;
import com.sun.jdi.event.VMDeathEvent;
import com.sun.jdi.event.VMDisconnectEvent;
import com.sun.jdi.event.VMStartEvent;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.ExceptionRequest;
import com.sun.jdi.request.MethodEntryRequest;
import com.sun.jdi.request.MethodExitRequest;
import com.sun.jdi.request.ModificationWatchpointRequest;
import com.sun.jdi.request.StepRequest;
import com.sun.jdi.request.ThreadDeathRequest;


/**
 * This class processes incoming JDI events and displays them
 *
 * @version     @(#) EventThread.java 1.6 05/05/2010 12:48:13
 * @author Ben d'Hont (Robert Field)
 * 
 */
public class EventThread extends Thread {

    private final VirtualMachine vm;   	// Running VM
    private final String[] excludes;   	// Packages to exclude
    private final PrintWriter writer;  	// Where output goes

    static String nextBaseIndent = ""; 	// Starting indent for next thread

    private boolean connected = true;  	// Connected to VM
    private boolean vmDied = true;     	// VMDeath occurred
    private Map<ThreadReference, ThreadTrace> traceMap = new HashMap<ThreadReference, ThreadTrace>();// Maps ThreadReference to ThreadTrace instances
    
    //added
    private boolean mainAdded = false; 	// adds the class with the main function once to the list of objects
    private DataFacade dataFacade;
    private HashMap<Integer, HashMap<Field, Value>> objectDatastructures = new HashMap<Integer, HashMap<Field, Value>>();
    
    /**
     * Constructor 
     * 
     * @param vm		= Virtual Machine in which the external application will be started
     * @param excludes  = Class patterns for which we don't want events (like javax.*, java.*)
     * @param writer	= Object to writ the output trough (is not in use right now)
     * @param dataFacade= DataFacade to which all information will be send
     */
    public EventThread(VirtualMachine vm, String[] excludes, PrintWriter writer, DataFacade dataFacade) {
        super("event-handler");
        this.vm = vm;
        this.excludes = excludes;
        this.writer = writer;
        this.dataFacade = dataFacade;
    }

    /**
     * Run the event handling thread.  
     * As long as we are connected, get event sets off 
     * the queue and dispatch the events within them.
     */
    public void run() { 
    	EventQueue queue = vm.eventQueue();
        while (connected) {
            try {
                EventSet eventSet = queue.remove();
                EventIterator it = eventSet.eventIterator();
                while (it.hasNext()) {
                    handleEvent(it.nextEvent());
                }
                eventSet.resume();
            } catch (InterruptedException exc) {
                // Ignore
            } catch (VMDisconnectedException discExc) {
                handleDisconnectedException();
                break;
            }
        }
    }

    /**
     * Create the desired event requests, and enable 
     * them so that we will get events.
     * @param excludes     Class patterns for which we don't want events
     * @param watchFields  Do we want to watch assignments to fields
     */
    public void setEventRequests(boolean watchFields) {
        EventRequestManager mgr = vm.eventRequestManager();
        
        // want all exceptions
        ExceptionRequest excReq = mgr.createExceptionRequest(null, true, true); 
        // suspend so we can step
        excReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);
        excReq.enable();

        MethodEntryRequest menr = mgr.createMethodEntryRequest();
        for (int i=0; i<excludes.length; ++i) {
            menr.addClassExclusionFilter(excludes[i]);
        }
        menr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
        menr.enable();

        MethodExitRequest mexr = mgr.createMethodExitRequest();
        for (int i=0; i<excludes.length; ++i) {
            mexr.addClassExclusionFilter(excludes[i]);
        }
       
        mexr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
        mexr.enable();

        ThreadDeathRequest tdr = mgr.createThreadDeathRequest();
	// Make sure we sync on thread death
        tdr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
        tdr.enable();

		if (watchFields) {
		    ClassPrepareRequest cpr = mgr.createClassPrepareRequest();
		    for (int i=0; i<excludes.length; ++i) {
			  cpr.addClassExclusionFilter(excludes[i]);
		    }
		    cpr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
		    cpr.enable();
		}
    }

    /**
     * This class keeps context on events in one thread.
     * In this implementation, context is the indentation prefix.
     */
    class ThreadTrace {
        final ThreadReference thread;
        final String baseIndent;
        static final String threadDelta = "                ";
        StringBuffer indent;

        ThreadTrace(ThreadReference thread) {
            this.thread = thread;
            this.baseIndent = nextBaseIndent;
		    indent = new StringBuffer(baseIndent);
		    nextBaseIndent += threadDelta;
		    println("====== " + thread.name() + " ======");
        }

		private void println(String str) {
		    writer.print(indent);
		    writer.println(str);
		}
	
		/**
		 * Read event to trace calling object, method name, called object
		 * @param event = event that is called when a method is entered
		 */
		public void methodEntryEvent(MethodEntryEvent event)  {
			Object object;
			int objectID =0;
			//get object reference 
			try {
				if (event.thread().frame(0).thisObject() != null) {
					object = event.thread().frame(0).thisObject();
					objectID = object.hashCode();
				}
				else
				{
					objectID = 0;
					if(mainAdded == false)	mainAdded = true;
				}
			} catch (IncompatibleThreadStateException e) {
				e.printStackTrace();
			}
			
			//check if a new object is created 
			if(event.method().name().equals("<init>"))
			{
				String[] fullName = event.method().declaringType().name().split("\\."); //package and classname
				String className = fullName[(fullName.length-1)]; //classname
				List<Method> methods = event.method().declaringType().allMethods();
				List<Field> fields = event.method().declaringType().allFields();
				dataFacade.objectInit(objectID, className, methods, fields);
			}
			
			//send information
			dataFacade.procesMethodcall(event.method(), event.method().declaringType().name(), objectID);
			println(event.method().name() + "  --  " + event.method().declaringType().name());
		    indent.append("| ");
		}
		
		/**
		 * Retrieve the content of all datastructures in this object
		 * and check the relations
		 * @param event = event that is called when a method is exited
		 */
		public void methodExitEvent(MethodExitEvent event)  {
			int objectID = -1;
			ObjectReference ot = null;
			ReferenceType rt = event.method().declaringType();
			
			try {
				if(event.thread().frame(0).thisObject() != null){
					objectID = event.thread().frame(0).thisObject().hashCode();
					ot = event.thread().frame(0).thisObject();
				}
			} catch (IncompatibleThreadStateException e1) {
				// TODO Auto-generated catch block
				//e1.printStackTrace();
			}
	
			getDataStructureContent(objectID, ot, rt);
			dataFacade.popObjectStack();
		    indent.setLength(indent.length()-2);		   	
		}
		
		// Fieldvalue initialised or edited
		public void fieldWatchEvent(ModificationWatchpointEvent event)  {
			Field field = event.field();
	        Value value = event.valueToBe();

			if(value != null && (value.toString().contains("java.util.ArrayList")
					|| value.toString().contains("java.util.HashMap")
					|| value.toString().contains("java.util.Hashtable")
					|| value.toString().contains("java.util.Stack")
					|| value.toString().contains("java.util.LinkedList")
					|| value.toString().contains("java.util.TreeMap")
					|| value.toString().contains("java.util.HashSet")					
					))
			{
				addDataStructure(dataFacade.getObjectStack().lastElement().getId(), value, field);
			}
			
//			if(field.typeName().toString().contains("java.util.List")
//					|| field.typeName().toString().contains("java.util.HashMap")
//					|| field.typeName().toString().contains("java.util.Stack")
//					|| field.typeName().toString().contains("java.util.Queue")
//					)
//			{
////				EventRequestManager mgr = vm.eventRequestManager();
////				ModificationWatchpointRequest mwr = mgr.createModificationWatchpointRequest(field);
////				mwr.enable();
//				//;
//				addDataStructure(dataFacade.getObjectStack().lastElement().getId(), value, field);
//			}
//			
	
		//tijdelijke oplossing voor enkele bugs   
		if(value != null && !value.toString().contains("javax.") && !value.toString().contains("java.") && !value.toString().contains("com.sun") && !value.toString().contains("sun"))
		{
	        if(value.toString().startsWith("instance of "))
	        {
	    		/*
	        	 * example   instance of testapps.Message(id=64)
	        	 * explained <initialisatie> <klasse> <object-id>        	
	        	 * splitten van de string om de naam te achterhalen
	        	 */        	
//	        	String objectName = value.toString().subSequence(12, value.toString().indexOf("(")).toString();
	        	int objectId = value.hashCode();
	        	dataFacade.addField(field, objectId);
	        	dataFacade.addRelation(objectId);
	    	}
	        else
	        {
	        	dataFacade.addField(field, value);
	        }
	    }
			println("    " + field.name() + " = " + value);
		}
	
		public void exceptionEvent(ExceptionEvent event) {
			println("Exception: " + event.exception() + " catch: " + event.catchLocation());
	
	        // Step to the catch
	        EventRequestManager mgr = vm.eventRequestManager();
	        StepRequest req = mgr.createStepRequest(thread, 
	        											StepRequest.STEP_LINE,	                                                    
	                                                    StepRequest.STEP_INTO);
	        req.addCountFilter(1);  // next step only
	        req.setSuspendPolicy(EventRequest.SUSPEND_ALL);
	        req.enable();
		}
	
	        // Step to exception catch
		public void stepEvent(StepEvent event)  {			
	        // Adjust call depth
	        int cnt = 0;
	        indent = new StringBuffer(baseIndent);
	        try {
	            cnt = thread.frameCount();
	        } catch (IncompatibleThreadStateException exc) {
	        }
	        while (cnt-- > 0) {
	           indent.append("| ");
	        }
	        
	        EventRequestManager mgr = vm.eventRequestManager();
	        mgr.deleteEventRequest(event.request());
	    }
	
		public void threadDeathEvent(ThreadDeathEvent event)  {
	            indent = new StringBuffer(baseIndent);
		    println("====== " + thread.name() + " end ======");
        }
    }
    
    /**
     * Returns the ThreadTrace instance for the specified thread,
     * creating one if needed.
     */
    ThreadTrace threadTrace(ThreadReference thread) {
    	
    	ThreadTrace trace = (ThreadTrace)traceMap.get(thread);
		if (trace == null) {
		    trace = new ThreadTrace(thread);
		    traceMap.put(thread, trace);
		}
		return trace;
    }

    /**
     * Dispatch incoming events
     */
    private void handleEvent(Event event) {
//    	if(event instanceof Locatable)
//    	{
//    		eventLocation(event);
//    	}
    	if (event instanceof ExceptionEvent) {
            exceptionEvent((ExceptionEvent)event);
        } else if (event instanceof ModificationWatchpointEvent) {
            fieldWatchEvent((ModificationWatchpointEvent)event);
        } else if (event instanceof MethodEntryEvent) {
            methodEntryEvent((MethodEntryEvent)event);
        } else if (event instanceof MethodExitEvent) {
            methodExitEvent((MethodExitEvent)event);
        } else if (event instanceof StepEvent) {
        	stepEvent((StepEvent)event);
        } else if (event instanceof ThreadDeathEvent) {
            threadDeathEvent((ThreadDeathEvent)event);
        } else if (event instanceof ClassPrepareEvent) {
        	classPrepareEvent((ClassPrepareEvent)event);            
        } else if (event instanceof VMStartEvent) {
        	vmStartEvent((VMStartEvent)event);
        } else if (event instanceof VMDeathEvent) {
            vmDeathEvent((VMDeathEvent)event);
        } else if (event instanceof VMDisconnectEvent) {
            vmDisconnectEvent((VMDisconnectEvent)event);
        } else {
            throw new Error("Unexpected event type");
        }
    }
    
    /**
     * 
     * @param event = The linenumber (sourcecode) of this event will be fetch if possible
     */
    private void eventLocation(Event event)
    {
		Locatable loc = ((Locatable) event);
		Location location = ((Locatable) event).location(); 
		try { 
			String fnm = location.sourceName();  // get code's filename
			dataFacade.showCode(fnm, location);
			if(location.lineNumber() > 0)  System.out.println("linenumber: "+location);
			//linenumbers++;
			//System.err.println(";"+loc.location());
		} 
		catch (AbsentInformationException e) {}
    }

    /***
     * A VMDisconnectedException has happened while dealing with
     * another event. We need to flush the event queue, dealing only
     * with exit events (VMDeath, VMDisconnect) so that we terminate
     * correctly.
     */
    public synchronized void handleDisconnectedException() {
        EventQueue queue = vm.eventQueue();
        while (connected) {
            try {
                EventSet eventSet = queue.remove();
                EventIterator iter = eventSet.eventIterator();
                while (iter.hasNext()) {
                    Event event = iter.nextEvent();
                    if (event instanceof VMDeathEvent) {
                        vmDeathEvent((VMDeathEvent)event);
                    } else if (event instanceof VMDisconnectEvent) {
                        vmDisconnectEvent((VMDisconnectEvent)event);
                    } 
                }
                eventSet.resume(); // Resume the VM
            } catch (InterruptedException exc) {
                // ignore
            }
        }
    }

    private void vmStartEvent(VMStartEvent event)  {
         writer.println("-- VM Started --");
    }

    // Forward event for thread specific processing
    private void methodEntryEvent(MethodEntryEvent event)  {
         threadTrace(event.thread()).methodEntryEvent(event);
    }

    // Forward event for thread specific processing
    private void methodExitEvent(MethodExitEvent event)  {
         threadTrace(event.thread()).methodExitEvent(event);
    }

    // Forward event for thread specific processing
    private void stepEvent(StepEvent event)  {
    	threadTrace(event.thread()).stepEvent(event);
    }

    // Forward event for thread specific processing
    private void fieldWatchEvent(ModificationWatchpointEvent event)  {
         threadTrace(event.thread()).fieldWatchEvent(event);
    }

    void threadDeathEvent(ThreadDeathEvent event)  {
		ThreadTrace trace = (ThreadTrace)traceMap.get(event.thread());
		if (trace != null) {  // only want threads we care about
            trace.threadDeathEvent(event);   // Forward event
        }
    }

    /**
     * A new class has been loaded.  
     * Set watchpoints on each of its fields
     */
    private void classPrepareEvent(ClassPrepareEvent event)  {
        EventRequestManager mgr = vm.eventRequestManager();
		List<Field> fields = event.referenceType().visibleFields();
		for (Field field : fields){
		//for (Iterator<Field> it = fields.iterator(); it.hasNext(); ) {
		    //Field field = (Field)it.next();
		    ModificationWatchpointRequest req = mgr.createModificationWatchpointRequest(field);
	            for (int i=0; i<excludes.length; ++i) {
	                req.addClassExclusionFilter(excludes[i]);
	            }
		    req.setSuspendPolicy(EventRequest.SUSPEND_NONE);
		    req.enable();
		}
		//inladen van de broncode
    	String fnm; 
    	try { 
    	  fnm = event.referenceType().sourceName();   // get filename of the class 
    	  //System.out.println(fnm);
    	  dataFacade.addSourceFile(fnm);
//    	  sourceFileLoader.add(fnm); 
    	} 
    	catch (AbsentInformationException e)  
    	{  fnm = "??"; }
    }

    private void exceptionEvent(ExceptionEvent event) {
		ThreadTrace trace = (ThreadTrace)traceMap.get(event.thread());
		if (trace != null) {  // only want threads we care about
	            trace.exceptionEvent(event);      // Forward event
	        }
	    }

    public void vmDeathEvent(VMDeathEvent event) {
		vmDied = true;
	        writer.println("-- The application exited --");
	    }

    public void vmDisconnectEvent(VMDisconnectEvent event) {
        connected = false;
        if (!vmDied) {
        	writer.println("-- The application has been disconnected --");
        }
    }
    
    /*
     * 
     * Datastructuren
     * 
     */
    private void addDataStructure(int objectid, Value value, Field field)
    {
		HashMap<Integer, HashMap<Field, Value>> map = (HashMap<Integer, HashMap<Field, Value>>) objectDatastructures;
    	boolean added = false;
    	
		for(Entry<Integer, HashMap<Field, Value>> entry : map.entrySet())
    	{	        		
        	if(objectid == entry.getKey())
        	{
        		entry.getValue().put(field, value);
        		added = true;
        		break;
        	}
    	}
		if(!added)
		{
			HashMap<Field,Value> hashMap = new HashMap<Field,Value>();
			hashMap.put(field, value);
			objectDatastructures.put(objectid, hashMap);
		}
    }
    
    private void getDataStructureContent(int objectid, ObjectReference or, ReferenceType rt)
    {
    	dataFacade.removeRelations(objectid);
    	//fields opvragen
		for(Field field : rt.allFields())
		{
			if(or!=null){ // null = class with main method
				Value value = or.getValue(field);
				if(value != null && value.hashCode() != 0)
				{
					dataFacade.reAddRelation(objectid, value.hashCode());
				}
				
			}			
		}
    	
    	//datastructuren opvragen
    	HashMap<Integer,HashMap<Field,Value>> map = (HashMap<Integer, HashMap<Field,Value>>) objectDatastructures;
    	for(Entry<Integer, HashMap<Field,Value>> entry : map.entrySet())
    	{
        	if(objectid == entry.getKey())
        	{
        		for(Entry<Field,Value> hashmapFieldValue : entry.getValue().entrySet())
        		{
        			Field field = hashmapFieldValue.getKey();
        			Value value = hashmapFieldValue.getValue();
        			String valuesID = "";
        			if(value.toString().startsWith("instance of java.util.ArrayList"))
        			{
        				valuesID = getDataArrayList(objectid, value);
        			}
        			else if(value.toString().startsWith("instance of java.util.HashMap"))
        			{
        				valuesID = getDataHash(objectid, value, "HashMap");
        			}
        			else if(value.toString().startsWith("instance of java.util.Hashtable"))
        			{
        				valuesID = getDataHash(objectid, value, "Hashtable");
        			}
        			else if(value.toString().startsWith("instance of java.util.HashSet"))
        			{
        				valuesID = getDataHash(objectid, value, "HashSet");
        			}       
        			else if(value.toString().startsWith("instance of java.util.Stack"))
        			{
        				valuesID = getDataStack(objectid, value);
        			}
        			else if(value.toString().startsWith("instance of java.util.LinkedList"))
        			{
        				valuesID = getDataLinkedList(objectid, value);
        			}
        			else if(value.toString().startsWith("instance of java.util.TreeMap"))
        			{
        				valuesID = getTreeMap(objectid, value);
        			}
        			dataFacade.addFieldId(objectid, field, valuesID);
        		}
        		break;
        	}
    	}
    }
    
    /**
     * Method for getting the values of an ArrayList
     * @param objectid	|| id of the declaringtype of the datastructure 
     * @param value		|| datastructure jdi.Value (ArrayList)
     * @return 
     */
    private String getDataArrayList(int objectid, Value value)
    {
    	ObjectReference t = (ObjectReference) value;
		Value value2 = t.getValue(t.referenceType().fieldByName("elementData"));
		ArrayReference r = (ArrayReference) value2;
		String valuesID = "";
		if(r.getValues().size() > 0)
		{			
			for(Object o : r.getValues())
			{				
				if(o != null && o.toString().startsWith("instance of"))
				{					
					boolean bool = dataFacade.reAddRelation(objectid, o.hashCode());
					if(bool) valuesID += o.hashCode()+"|";
				}				
			}
		}
		return valuesID;
    }
    
    /**
     * Method for getting the values of an HashMap
     * @param objectid	|| id of the declaringtype of the datastructure 
     * @param value		|| datastructure jdi.Value (HashMap)
     */
    private String getDataHash(int objectid, Value value, String type)
    {
    	Value value2 = null;
    	
    	ObjectReference t = (ObjectReference) value;
    	if(type == "HashSet") // if the Hash is a hashset
    	{
        	ObjectReference map = (ObjectReference)t.getValue(t.referenceType().fieldByName("map"));
        	value2 = map.getValue(map.referenceType().fieldByName("table"));
    	}
    	else //example a hashtable
    	{
    		value2 = t.getValue(t.referenceType().fieldByName("table"));
    	}
    	ArrayReference r = (ArrayReference) value2;
    	String valuesID = "";
		if(r.getValues().size() > 0)
		{
			for(Object o : r.getValues())
			{
				if(o != null)
				{
					ObjectReference hashMapEntryOR = (ObjectReference) o;
					Value entryKey = hashMapEntryOR.getValue(hashMapEntryOR.referenceType().fieldByName("key"));
					Value hashMapValue = hashMapEntryOR.getValue(hashMapEntryOR.referenceType().fieldByName("value"));
					ObjectReference objectKey = (ObjectReference) entryKey;
					ObjectReference objectValue = (ObjectReference) hashMapValue;
					if(objectKey != null && objectKey.toString().startsWith("instance of"))
					{
						boolean bool = dataFacade.reAddRelation(objectid, objectKey.hashCode());
						if(bool) valuesID += objectKey.hashCode()+"|";
					}
					if(objectValue != null && objectValue.toString().startsWith("instance of"))
					{
						boolean bool = dataFacade.reAddRelation(objectid, objectValue.hashCode());
						if(bool) valuesID += objectValue.hashCode()+"|";
					}
				}
			}
		}
		return valuesID;
    }
    
    /**
     * Method for getting the values of an Stack
     * @param objectid	|| id of the declaringtype of the datastructure 
     * @param value		|| datastructure jdi.Value (Stack)
     */
    private String getDataStack(int objectid, Value value)
    {
    	ObjectReference t = (ObjectReference) value;
		Value value2 = t.getValue(t.referenceType().fieldByName("elementData"));
		ArrayReference r = (ArrayReference) value2;
		String valuesID = "";
		if(r.getValues().size() > 0)
		{
			for(Object o : r.getValues())
			{
				if(o != null && o.toString().startsWith("instance of"))
				{
					boolean bool = dataFacade.reAddRelation(objectid, o.hashCode());
					if(bool) valuesID += o.hashCode()+"|";
				}
			}
		}
		return valuesID;
    }
    
    /**
     * Method for getting the values of an linkedlist
     * @param objectid 	|| id of the declaringtype of the datastructure 
     * @param value		|| datastructure jdi.Value (linkedList)
     */
    private String getDataLinkedList(int objectid, Value value)
    {
    	ObjectReference linkedListOR = (ObjectReference) value;
    	//Value v = (Value)linkedListOR.getValue(linkedListOR.referenceType().fieldByName("size"));
    	ObjectReference linkedListEntry = (ObjectReference)linkedListOR.getValue(linkedListOR.referenceType().fieldByName("header"));
    	ObjectReference linkedListEntryFirstElement = (ObjectReference)linkedListEntry.getValue(linkedListEntry.referenceType().fieldByName("next"));
    	String tempValues = "";
    	String valuesID = readLinkedListEntry(objectid, (ObjectReference) linkedListEntryFirstElement, (ObjectReference) linkedListEntry, tempValues);
    	return valuesID;
    }
    
    /**
     * Method for reading out the entry of a linkedList
     * @param objectId        || id of the declaringtype of the datastructure 
     * @param linkedListEntry || first linkedListEntry with a element value 
     * @param firstEntry	  || first linkedListEntry (for checking if every listEntry has been visited)
     */
    private String readLinkedListEntry(int objectId, ObjectReference linkedListEntry, ObjectReference firstEntry, String tempValues)
    {   
    	if(linkedListEntry == firstEntry){}
    	else if(linkedListEntry.getValue(linkedListEntry.referenceType().fieldByName("next")) != null)
    	{
    		//System.out.println("linkedListEntry: "+linkedListEntry.getValue(linkedListEntry.referenceType().fieldByName("element")));
    		if(linkedListEntry.getValue(linkedListEntry.referenceType().fieldByName("element")) != null)
    		{
    			int entryElementHash = linkedListEntry.getValue(linkedListEntry.referenceType().fieldByName("element")).hashCode(); 
    			boolean bool = dataFacade.reAddRelation(objectId, entryElementHash);
    			if(bool) tempValues += entryElementHash+"|";
    		}
    		readLinkedListEntry(objectId, (ObjectReference) linkedListEntry.getValue(linkedListEntry.referenceType().fieldByName("next")), firstEntry, tempValues);
    	}
    	return tempValues;
    }
    
    private String getTreeMap(int objectId, Value value)
    {
    	//TreeMap
    	ObjectReference t = (ObjectReference) value;
    	ObjectReference treeMapEntry = (ObjectReference)t.getValue(t.referenceType().fieldByName("root"));
    	String tempValues = "";
    	String valuesID = readTreeMapEntry(objectId, treeMapEntry, tempValues);
    	return valuesID;
    }
    
    /**
     * Read out an TreeMapEntry
     * @param objectId		|| Objectid of the object that has the TreeMap as a field
     * @param treeMapEntry  || TreemapEntry of the datastructure TreeMap
     */
    private String readTreeMapEntry(int objectId, ObjectReference treeMapEntry, String tempValues)
    {
    	Value key = null;
    	Value value = null;
    	if(treeMapEntry != null)
    	{
	    	key = treeMapEntry.getValue(treeMapEntry.referenceType().fieldByName("key"));
	    	value = treeMapEntry.getValue(treeMapEntry.referenceType().fieldByName("value"));
    	}
    	if(key != null)
    	{
    		boolean bool = dataFacade.reAddRelation(objectId, key.hashCode());
    		if(bool) tempValues += key.hashCode()+"|";
   			bool = dataFacade.reAddRelation(objectId, value.hashCode());
   			if(bool) tempValues += value.hashCode()+"|";
    		if(treeMapEntry.getValue(treeMapEntry.referenceType().fieldByName("right")) != null)
    		{
    			readTreeMapEntry(objectId, (ObjectReference) treeMapEntry.getValue(treeMapEntry.referenceType().fieldByName("right")),tempValues);
    		}
    		if(treeMapEntry.getValue(treeMapEntry.referenceType().fieldByName("left")) != null)
    		{
    			readTreeMapEntry(objectId, (ObjectReference) treeMapEntry.getValue(treeMapEntry.referenceType().fieldByName("left")),tempValues);
    		}
    	}
    	return tempValues;
    }
}
